package com.sevenpay.wx.connection;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * HttpClient，封装http和https的post操作，启动httpConnectionPool
 * 
 * @author likunwang
 *
 */
public class HttpClient {

	private static Logger logger = LoggerFactory.getLogger(HttpClient.class);
	
	private CloseableHttpClient httpClient = null;
	private int connectionRequestTimeout;
	private int connectTimeout;
	private int socketTimeout;
	private int maxTotal;
	private int defaultMaxPerRoute;

	public HttpClient(int connectionRequestTimeout, int connectTimeout, int socketTimeout, int maxTotal, int defaultMaxPerRoute) {
		super();
		this.connectionRequestTimeout = connectionRequestTimeout;
		this.connectTimeout = connectTimeout;
		this.socketTimeout = socketTimeout;
		this.maxTotal = maxTotal;
		this.defaultMaxPerRoute = defaultMaxPerRoute;
	}

	public void init() {
		
		PoolingHttpClientConnectionManager connectionManager;
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				// 信任所有
				public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
					return true;
				}

			}).build();
			
			connectionManager = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
			        .register("http", PlainConnectionSocketFactory.getSocketFactory())
			        .register("https", new SSLConnectionSocketFactory(sslContext))
			        .build());
			
		} catch (Exception e) {
			logger.error("初始化HttpClient异常：" + e.getMessage(), e);
			connectionManager = new PoolingHttpClientConnectionManager();
		}
		
		connectionManager.setMaxTotal(this.maxTotal);
		connectionManager.setDefaultMaxPerRoute(this.defaultMaxPerRoute);
		
		httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
		
	}
	
	public void close() {
		if (null != httpClient) {
			try {
				httpClient.close();
			} catch (IOException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}
	
	/**
	 * form表单提交
	 * @param url
	 * @param data
	 * @param encoding
	 * @return
	 * @throws Exception
	 */
	public String send(String url, Map<String, String> data, String encoding) throws Exception {
		
		String reqMsg = getRequestParamString(data, encoding);
		logger.info("MyHttpClient请求报文：{}", reqMsg);

		if(url.startsWith("https")) {
			return httpsPost(url, reqMsg, encoding);
		} else {
			return httpPost(url, reqMsg, encoding);
		}

	}
	
	/**
	 * post方式提交表单
	 * 
	 * @param url
	 * @param reqMsg： key1=value1&key2=value2...
	 * @param encoding
	 * @return
	 * @throws Exception
	 */
	public String send(String url, String reqMsg, String encoding) throws Exception {
		
		logger.info("MyHttpClient请求报文：{}", reqMsg);

		if(url.startsWith("https")) {
			return httpsPost(url, reqMsg, encoding);
		} else {
			return httpPost(url, reqMsg, encoding);
		}

	}
	
	/**
	 * 数据字节流提交
	 * 
	 * @param url
	 * @param bytes
	 * @return
	 * @throws Exception
	 */
	public String sendBytes(String url, byte[] bytes, String encoding) throws Exception {
		

		if(url.startsWith("https")) {
			return httpsPost(url, bytes, encoding);
		} else {
			return httpPost(url, bytes, encoding);
		}

	}
	
	/**
	 * 基于http的post方式
	 * 
	 * @param url
	 * @param reqMsg
	 * @return
	 * @throws Throwable
	 */
	private String httpPost(String url, String reqMsg, String encoding) throws Exception {
		
		Response response = Executor.newInstance(httpClient).execute(Request.Post(url) // 请求URL
				.addHeader("Content-Type", "application/x-www-form-urlencoded") // 增加报文头
				.bodyString(reqMsg, ContentType.create("application/x-www-form-urlencoded", encoding))
				.connectTimeout(connectTimeout)
				.socketTimeout(socketTimeout));
		HttpResponse httpResponse = response.returnResponse();
		StatusLine statusLine = httpResponse.getStatusLine();
		logger.info(statusLine.toString());
		if (200 == statusLine.getStatusCode()) {
			String resMsg = IOUtils.toString(httpResponse.getEntity().getContent(), encoding);
			response.discardContent();
			return resMsg;
		} else {
			throw new RuntimeException("http交互失败, " + statusLine.toString());
		}
	}
	
	private String httpPost(String url, byte[] bytes, String encoding) throws Exception {
		
		Response response = Executor.newInstance(httpClient).execute(Request.Post(url) // 请求URL
				.addHeader("Content-Type", ContentType.APPLICATION_OCTET_STREAM.toString()) // 增加报文头
				.bodyByteArray(bytes)
				.connectTimeout(connectTimeout)
				.socketTimeout(socketTimeout));
		HttpResponse httpResponse = response.returnResponse();
		StatusLine statusLine = httpResponse.getStatusLine();
		logger.info(statusLine.toString());
		if (200 == statusLine.getStatusCode()) {
			String resMsg = IOUtils.toString(httpResponse.getEntity().getContent(), encoding);
			response.discardContent();
			return resMsg;
		} else {
			throw new RuntimeException("http交互失败, " + statusLine.toString());
		}
	}
	
	private String httpsPost(String url, String reqMsg, String encoding) throws Exception {

		String resMsg = null;

		CloseableHttpResponse response = null;

		try {

			HttpPost httpPost = new HttpPost(url);
			// 增加报文头
			httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
			httpPost.setEntity(new StringEntity(reqMsg, ContentType.create("application/x-www-form-urlencoded", encoding)));
			httpPost.setConfig(RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build());
			response = httpClient.execute(httpPost);

			logger.info(response.getStatusLine().toString());

			if (200 == response.getStatusLine().getStatusCode()) {
				HttpEntity entity = response.getEntity();

				resMsg = IOUtils.toString(entity.getContent(), encoding);

				EntityUtils.consume(entity);
			} else {
				throw new RuntimeException("https交互失败, " + response.getStatusLine().toString());
			}

		} catch (Throwable t) {
			throw t;
		} finally {
			try {
				if (null != response) {
					response.close();
				}
			} catch (Exception e) {
				logger.error("关闭http响应对象异常:" + e.getMessage(), e);
			}
		}
		return resMsg;
	}
	
	private String httpsPost(String url, byte[] bytes, String encoding) throws Exception {

		String resMsg = null;

		CloseableHttpResponse response = null;

		try {

			HttpPost httpPost = new HttpPost(url);
			// 增加报文头
			httpPost.setHeader("Content-Type", ContentType.APPLICATION_OCTET_STREAM.toString());
			httpPost.setEntity(new ByteArrayEntity(bytes, null));
			httpPost.setConfig(RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build());
			response = httpClient.execute(httpPost);

			logger.info(response.getStatusLine().toString());

			if (200 == response.getStatusLine().getStatusCode()) {
				HttpEntity entity = response.getEntity();

				resMsg = IOUtils.toString(entity.getContent(), encoding);

				EntityUtils.consume(entity);
			} else {
				throw new RuntimeException("https交互失败, " + response.getStatusLine().toString());
			}

		} catch (Throwable t) {
			throw t;
		} finally {
			try {
				if (null != response) {
					response.close();
				}
			} catch (Exception e) {
				logger.error("关闭http响应对象异常:" + e.getMessage(), e);
			}
		}
		return resMsg;
	}

	private String getRequestParamString(Map<String, String> requestParam, String coder) {
		if ((coder == null) || ("".equals(coder))) {
			coder = "UTF-8";
		}
		StringBuffer sf = new StringBuffer("");
		String reqstr = "";
		if ((requestParam != null) && (requestParam.size() != 0)) {
			for (Map.Entry<String, String> en : requestParam.entrySet()) {
				try {
					sf.append((String) en.getKey() + "=" + ((en.getValue() == null) || ("".equals(en.getValue())) ? "" : URLEncoder.encode((String) en.getValue(), coder)) + "&");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
					return "";
				}
			}
			reqstr = sf.substring(0, sf.length() - 1);
		}
		logger.info("请求报文(已做过URLEncode编码):[" + reqstr + "]");
		return reqstr;
	}

	public int getConnectionRequestTimeout() {
		return connectionRequestTimeout;
	}

	public void setConnectionRequestTimeout(int connectionRequestTimeout) {
		this.connectionRequestTimeout = connectionRequestTimeout;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public int getSocketTimeout() {
		return socketTimeout;
	}

	public void setSocketTimeout(int socketTimeout) {
		this.socketTimeout = socketTimeout;
	}
	
}
